/*
  Copyright (c) 2002, 2003 Russell Morris
  All rights reserved.

  Redistribution and use in source and binary forms, with or without modification, 
  are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice, 
      this list of conditions and the following disclaimer. 
      
    * Redistributions in binary form must reproduce the above copyright notice, 
      this list of conditions and the following disclaimer in the documentation 
      and/or other materials provided with the distribution. 
      
    * The names of the contributors may not be used to endorse or promote 
      products derived from this software without specific prior written permission. 
      
    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
    ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 
    IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
    THE POSSIBILITY OF SUCH DAMAGE.
*/

using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using System.Drawing;
using System.Drawing.Design;
using System.Resources;
using System.Reflection;

namespace WRM.Windows.Forms.Design
{
  /// <summary>
	/// A root-level designer that allows any <see cref="PropertyPane"/>-derived
	/// class to be designed in the same way that <see cref="UserControl"/>s are
	/// designed.
	/// </summary>
	/// <remarks>
	/// <para>
	/// This root designer will attempt to create create a <see cref="ToolboxItem"/> 
	/// on VS.NET's ToolBox in a group called "PropertyPanes".  The 
	/// <see cref="PropertyPaneRootDesigner.GetToolboxBitmap"/> method will search
	/// for an appropriate 16x16 bitmap to use as the <c>ToolboxItem</c>'s icon.
	/// </para>
	/// </remarks>
  internal class PropertyPaneRootDesigner : DocumentDesigner
  {
    /// <summary>
    /// A reference to the <see cref="IDesignerHost"/> that hosts this
    /// instance
    /// </summary>
    private IDesignerHost mHost;

   
    /// <summary>
    /// Default constructor
    /// </summary>
    public PropertyPaneRootDesigner() : base()
    {
    }
     
    /// <summary>
    /// Handles the <see cref="IDesignerHost.LoadComplete"/> event.
    /// </summary>
    /// <remarks>
    /// After the <c>PropertyPaneRootDesigner</c> is completely loaded 
    /// we can add a toolbox item to the toolbox.  
    /// We must wait until after the load completes because the fully qualified name
    /// of the base class is not known until that time.
    /// </remarks>
    /// <param name="sender">The object that fired this event</param>
    /// <param name="e">Event information</param>
    private void OnLoadComplete(object sender, EventArgs e) 
    {
      IDesignerHost host = (IDesignerHost)sender;
      host.LoadComplete -= new EventHandler(this.OnLoadComplete);
            
      // Now get a hold of the toolbox service and add an icon for our user control.  The
      // toolbox service will automatically maintain this icon as long as our file lives.
      //
      IToolboxService tbx = (IToolboxService)GetService(typeof(IToolboxService));

      Type paneType = host.RootComponent.GetType();
      
      // make sure we don't add a toolbox item for anything derived
      // from SharedPropertyPane
      if (tbx != null && 
        !paneType.Equals(typeof(SharedPropertyPane)) &&
        !paneType.IsSubclassOf(typeof(SharedPropertyPane)))
      {
        string fullClassName = host.RootComponentClassName;
        ToolboxItem item = new ToolboxItem();
        item.TypeName = fullClassName;
        int idx = fullClassName.LastIndexOf('.');
        if (idx != -1) 
        {
          item.DisplayName = fullClassName.Substring(idx + 1);
        }
        else 
        {
          item.DisplayName = fullClassName;
        }

        item.Bitmap = GetToolboxBitmap(Type.GetType(fullClassName),item.DisplayName);
        item.Lock();

        if(tbx.GetToolboxItems().Contains(item))
        {
          tbx.RemoveToolboxItem(item,"PropertyPanes");
        }
        tbx.AddLinkedToolboxItem(item, "PropertyPanes", host);

      }
      
    }

    /// <summary>
    ///   Get a Bitmap to be used on the toolbar
    /// </summary>
    /// <param name="t">The Type for which to find a Bitmap.</param>
    /// <param name="className">The class name for which to get a bitmap</param>
    /// <remarks>
    ///   <para>
    ///   The function will look for the appropriate bitmap by searching in 
    ///   the following ways:
    ///   </para>
    ///   <list type="number">
    ///     <item>
    ///       <term>[<paramref name="t"/>'s class name].bmp</term>
    ///       <description>
    ///         The algorithm next looks for a .bmp file in the assembly's
    ///         resources that has the same name as <paramref name="t"/>'s class name.
    ///       </description>
    ///     </item>
    ///     <item>
    ///       <term>Default</term>
    ///       <description>
    ///         If the two previous steps turn up nothing, the default
    ///         PropertyPane.bmp bitmap is used (the gray arrow).
    ///       </description>
    ///     </item>
    ///   </list>
    ///   <para>
    ///   The loading algorithm interprets bright green (RGB 0,255,0) as 
    ///   transparent.
    ///   </para>
    /// </remarks>
    /// <returns>
    /// Returns a <see cref="Bitmap"/> that is to be used as a Toolbox icon
    /// </returns>
    private Bitmap GetToolboxBitmap(Type t, string className)
    {
      Bitmap b;
      
      // Look for a bitmap ourselves
      //
      try
      {
        b = new Bitmap(t, className + ".bmp");
      }
      catch(Exception /*ex*/)
      {
        // On exception, use the default PropertyPane bitmap;
        b = new Bitmap(typeof(PropertyPane),"PropertyPane.bmp");
      }

      b.MakeTransparent(Color.FromArgb(0,255,0));
      return b;
    }

    /// <summary>
    /// Initializes this designer with the <see cref="PropertyPane"/> that
    /// it is designing.
    /// </summary>
    /// <param name="component">
    /// The <c>PropertyPane</c> this designer is designing
    /// </param>
    /// <exception cref="ArgumentNullException">
    /// Thrown if <paramref name="component"/> is null
    /// </exception>
    /// <exception cref="ArgumentException">
    /// Thrown if component does not descend from <see cref="PropertyPane"/>
    /// </exception>
    public override void Initialize(System.ComponentModel.IComponent component)
    {
      base.Initialize(component);

      if(component == null)
        throw new ArgumentNullException("component");

      if(!(component is PropertyPane))
        throw new ArgumentException(
          "PropertyPaneRootDesigner can only provide design-time services for " +
          "classes that descend from PropertyPane.  '" + component.GetType().FullName + "' " +
          "does not descend from PropertyPane.",
          "component");

      IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost));
      if (host != null) 
        host.LoadComplete += new EventHandler(OnLoadComplete);
    }

    /// <summary>
    /// Gets the <see cref="IDesignerHost"/> that hosts this designer
    /// </summary>
    /// <value>
    /// Uses <see cref="ComponentDesigner.GetService"/> to retreive an
    /// instance of an object that implements the
    /// <see cref="IDesignerHost"/> interface.
    /// </value>
    private IDesignerHost DesignerHost
    {
      get
      {
        if (mHost == null)
        {
          mHost = (IDesignerHost)GetService(typeof(IDesignerHost));
        }
        return mHost;
      }
    }
  }
}
